

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.event.*;

import java.text.*;
import java.awt.font.*;

import SSF.Net.*;
import SSF.OS.BGP4.*;
import SSF.OS.BGP4.Comm.*;
import SSF.OS.BGP4.Util.*;
import SSF.Util.Streams.*;
import SSF.OS.NetFlow.*;
import SSF.OS.NetFlow.Filter.*;
import com.renesys.raceway.DML.*;

import com.renesys.raceway.views.*;

import SSF.Util.Plot.*;

/** This Stream.Player implementation provides default 
 *  implementations for the describe() method (to 
 *  render records as strings) and the getControls() method.
 *  It will not be part of Raceway in the long run; instead, 
 *  it can be transitioned into SSF.Util.Plot. 
 * 
 *  Derived from droptailRecordPlayer_1, whose original documentation 
 *  reads as follows:
 *
 *  <pre>
 *  Standalone class to translate Streams containing QueueRecord:s
 *  generated by droptailQueueMonitor_1 into ASCII format for viewing.
 *  @author Michael Liljenstam
 *  </pre>
 */
public class droptailPlayer implements Stream.Player {

    // attrs

    TimeSeriesComponent _tsc = null;


    //----------------------------------------------------------------------

    public droptailPlayer() {
	fm.setMinimumFractionDigits(3);
	fm.setMaximumFractionDigits(3);


	// create plot window

	int winHeight = 680; // pixels
	try {
	    _tsc = new TimeSeriesComponent(4, "queue monitor", winHeight);
	} catch (Exception e) {
	    System.err.println("ERROR: Couldn't create time-series plot window. Exiting.");
	    System.exit(1);
	}

	// hardcoded for now...

	_tsc.setTitle("NIC packet counts");
	_tsc.setBinSize(1.0F); //- optional, default
	_tsc.setTimeWindowSize(386); // moving window, time units

	_tsc.getPlot(0).setDatasetName("h1-1");
	_tsc.getPlot(0).setFieldSelectorName("pkt");
	_tsc.getPlot(1).setDatasetName("r1-0");
	_tsc.getPlot(1).setFieldSelectorName("pkt");
	_tsc.getPlot(2).setDatasetName("r1-0");
	_tsc.getPlot(2).setFieldSelectorName("qlen");
	_tsc.getPlot(3).setDatasetName("r1000-0");
	_tsc.getPlot(3).setFieldSelectorName("pkt");
    }

    //----------------------------------------------------------------------

    public static long MIN_DURATION = 10; // ms

    private void pulse(Raceway.VisibleHost h, double x) {
	long ms0 = System.currentTimeMillis();
	int lx = (int)(Math.log(x)/Math.log(10));
	int rx = (lx>0?25*lx:0); 
	if (rx>255) rx=255;
	Color c = new Color(rx,rx/2,rx/2);
	while (System.currentTimeMillis()-ms0 < MIN_DURATION) {
	    h.setFillColor(c);
	    h.scaleSize(2.);
	    h.refresh();
	    h.scaleSize(0.5);
	    h.refresh();
	}
    }

    public void play(Stream.Record r) {
	String rty = 
	    stream.getRecordTypeString(r.type_code);
	String nhi = 
	    stream.getRecordSourceString(r.source_code);
	if ("SSF.Net.QueueRecord_1".equals(rty) ||
	    "SSF.Net.QueueProbeIntRecord".equals(rty)) {

	    // animation
	    Raceway.VisibleHost h = stream.raceway().getVisibleHost(nhi);
            if (rty.equals("SSF.Net.QueueRecord_1"))
	       pulse(h,BytesUtil.bytesToInt(r.bytes,r.offset+0));
	    else 
	       pulse(h,1);

	    // time series plot
	    plotBar(r);
	}
    }

    public void plotBar(Stream.Record record) {

	int tid   = record.type_code;
	int sid    = record.source_code;
	double time  = record.timestamp;
	byte[] buf   = record.bytes;
	int offset   = record.offset;
	int length   = record.length;
	
	if ((stream.getRecordTypeString(tid).equals("SSF.Net.QueueRecord_1")) 
	    || (stream.getRecordTypeString(tid).equals("SSF.Net.QueueProbeIntRecord"))) {

	    String src_nhi = stream.getRecordSourceString(sid);

	    if (stream.getRecordTypeString(tid).equals("SSF.Net.QueueRecord_1")) {
		float qLength = BytesUtil.bytesToFloat(buf, offset+0);
		int pktCount = BytesUtil.bytesToInt(buf, offset+4);
		int pktDropCount = BytesUtil.bytesToInt(buf, offset+8);

		// time series

		if (src_nhi.startsWith("1:1")) {
		    // server host
		    _tsc.getPlot(0).addSinglePoint(time, pktCount);
		} else if (src_nhi.startsWith("1:0")) {
		    // first level router
		    _tsc.getPlot(1).addSinglePoint(time, pktCount); // pkts
		    _tsc.getPlot(2).addSinglePoint(time, qLength); // queue
		} if (src_nhi.startsWith("1000:")) {
		    // aggregate level router
		    _tsc.getPlot(3).addSinglePoint(time, pktCount);
		}
	    }
	}
    }


    public String toString() {
	return "Droptail Queue Event Monitor";
    }

    protected Stream stream;

    public void setStream(Stream stream) {
	this.stream = stream;
    }

    public void start(Stream stream) {
	this.stream = stream;
    }

    public void finish(Stream stream) {
	//this.stream = null; // too harsh
    }

    //----------------------------------------------------------------------
    // OPTIONS 

    boolean[] options;
    String nhiFilter;

    private JComponent controls;

    public synchronized Component getControls() {
	if (null==controls) {
	    JLabel sorry = new JLabel("No configurable content");
	    sorry.setForeground(Color.blue);
	    controls = new JPanel();
	    controls.add(sorry);
	}
	return controls;
    }

    //----------------------------------------------------------------------

  java.text.DecimalFormat fm = new DecimalFormat();


    public static final boolean IGNORE_ALL = false; // true;

    public AttributedString describe(Stream.Record record) {
	if (IGNORE_ALL)
	    return null;
	
	int tid   = record.type_code;
	int sid    = record.source_code;
	double time  = record.timestamp;
	byte[] buf   = record.bytes;
	int offset   = record.offset;
	int length   = record.length;
	
	String ret;
	Color newColor = Color.black;
	String src;
	String typ;

    //    queueRecTypeCode = getRecordTypeCode("SSF.Net.QueueProbeIntRecord");
    //    probeIntTypeCode = getRecordTypeCode("SSF.Net.QueueRecord_1");
    //    System.err.println("tid " + tid + " qrtc " + queueRecTypeCode +
    //		       " pitc " + probeIntTypeCode);
 
    //     if (tid == queueRecTypeCode ||  tid == probeIntTypeCode) {
     if ((stream.getRecordTypeString(tid).equals("SSF.Net.QueueRecord_1")) 
	|| (stream.getRecordTypeString(tid).equals("SSF.Net.QueueProbeIntRecord"))) {

     String src_nhi = stream.getRecordSourceString(sid);

     // 	     if (tid == queueRecTypeCode) {
     if (stream.getRecordTypeString(tid).equals("SSF.Net.QueueRecord_1")) {
        float qLength = BytesUtil.bytesToFloat(buf, offset+0);
	int pktCount = BytesUtil.bytesToInt(buf, offset+4);
	int pktDropCount = BytesUtil.bytesToInt(buf, offset+8);

 	ret = (fm.format(time) + " " + src_nhi
	       + " pkts "  + pktCount
	       + " drops " + pktDropCount
	       + " av_qlen " + qLength);


	// animation
	if (src_nhi.startsWith("1000:"))
	   newColor = new Color(255,127,127);
	else if (src_nhi.startsWith("1000:"))
	   newColor = new Color(127,255,127);
        else 
	   newColor = new Color(127,127,255);
      } else {
 	  //        double probeInt = BytesUtil.bytesToDouble(buf, 0);
 	  /** FIXME: bytesToDoulbe missing in bytes util */
        float probeInt = BytesUtil.bytesToFloat(buf, offset+0);

 	ret = (fm.format(time) + " " + src_nhi
	       + " probe_interval "  + probeInt);
	newColor = new Color(127,127,255);
      }
    } else {
	return null;
	//      System.err.println("Not a QueueRecord_1 record!");
	//      System.exit(1);
    } 
     
    AttributedString aret = new AttributedString(ret.trim());
    aret.addAttribute(TextAttribute.FOREGROUND, newColor);
    return aret;
  }
}
